package net.fkzg.framework.application;

import lombok.extern.slf4j.Slf4j;
import org.smartboot.http.common.enums.HeaderNameEnum;
import org.smartboot.http.common.enums.HttpMethodEnum;
import org.smartboot.http.common.enums.HttpStatus;
import org.smartboot.http.common.utils.AntPathMatcher;
import org.smartboot.http.common.utils.DateUtils;
import org.smartboot.http.common.utils.Mimetypes;
import org.smartboot.http.common.utils.StringUtils;
import org.smartboot.http.server.HttpRequest;
import org.smartboot.http.server.HttpResponse;
import org.smartboot.http.server.HttpServerHandler;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Date;

@Slf4j
public class DistStaticResourceHandler extends HttpServerHandler {
    private static final int READ_BUFFER = 1024 * 1024;
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    private static String staticDir = null;

    public DistStaticResourceHandler() {
        URL dist = DistStaticResourceHandler.class.getClassLoader().getResource("./dist");
        if (null != dist) {
            String path = dist.getPath();
            log.info("find: staticDir = {} ", path);
            staticDir = path;
        }
    }

    @Override
    public void handle(HttpRequest request, HttpResponse response) throws IOException {
        String uri = request.getRequestURI();
        String method = request.getMethod();
        log.debug("request Method: {} , URL: {}", method, uri);
        if (PATH_MATCHER.match("/api/**", uri)) {
            respNotFound(response);
            return;
        }
        handleDist(uri, method, request, response);
    }

    private void handleDist(String uri, String method, HttpRequest request, HttpResponse response) throws IOException {
        String fileName = request.getRequestURI();
        if (StringUtils.endsWith(fileName, "/")) {
            fileName += "index.html";
        }
        File file = new File(staticDir, URLDecoder.decode(fileName, "utf8"));
        //404
        if (!file.isFile()) {
            File indexHtmlFile = new File(staticDir, "/index.html");
            if (!indexHtmlFile.isFile()) {
                log.debug("try file: {} not found!", fileName);
                respNotFound(response);
                return;
            } else {
                log.debug("request url {} not found,try file index.html", uri);
                file = indexHtmlFile;
            }
        }
        //304
        Date lastModifyDate = new Date(file.lastModified());
        try {
            String requestModified = request.getHeader(HeaderNameEnum.IF_MODIFIED_SINCE.getName());
            if (StringUtils.isNotBlank(requestModified) && lastModifyDate.getTime() <= DateUtils.parseLastModified(requestModified).getTime()) {
                response.setHttpStatus(HttpStatus.NOT_MODIFIED);
                return;
            }
        } catch (Exception e) {
            log.error("exception", e);
        }
        response.setHeader(HeaderNameEnum.LAST_MODIFIED.getName(), DateUtils.formatLastModified(lastModifyDate));

        String contentType = Mimetypes.getInstance().getMimetype(file);
        response.setHeader(HeaderNameEnum.CONTENT_TYPE.getName(), contentType + "; charset=utf-8");
        //HEAD不输出内容
        if (HttpMethodEnum.HEAD.getMethod().equals(method)) {
            return;
        }

        if (!file.getName().endsWith("html") && !file.getName().endsWith("htm")) {
            response.setContentLength((int) file.length());
        }

        FileInputStream fis = new FileInputStream(file);
        FileChannel fileChannel = fis.getChannel();
        long fileSize = fileChannel.size();
        long readPos = 0;
        while (readPos < fileSize) {
            MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, readPos, fileSize - readPos > READ_BUFFER ? READ_BUFFER : fileSize - readPos);
            readPos += mappedByteBuffer.remaining();
            byte[] data = new byte[mappedByteBuffer.remaining()];
            mappedByteBuffer.get(data);
            response.write(data);
        }
        fileChannel.close();
        fis.close();

    }

    private static void respNotFound(HttpResponse response) {
        response.setHttpStatus(HttpStatus.NOT_FOUND);
        response.setHeader("X-Content-Type-Options", "nosniff");
        response.close();
    }
}
